//! Enum declaration parsers

use super::super::{parse_type, Parser};
use crate::ast::decl::*;
use crate::ast::StructField;
use crate::error::{CompilerError, Result};
use crate::lexer::token::Token;
use super::helpers::parse_where_clause_for_generics;

/// Parse enum declaration (type EnumName enum { ... })
pub fn parse_enum_declaration(parser: &mut Parser) -> Result<EnumDecl> {
    parser.expect(&Token::Type)?;

    if let Some(Token::Identifier(name)) = parser.peek().map(|t| &t.token) {
        let enum_name = name.clone();
        parser.advance()?;
        parser.expect(&Token::Enum)?;

        parse_enum(parser, enum_name)
    } else {
        Err(CompilerError::syntax(
            parser.current_location().line,
            parser.current_location().column,
            "Expected enum name after 'type'",
        ))
    }
}

/// Parse enum declaration from current position (assumes 'type' and name are already consumed)
pub fn parse_enum_declaration_from_current(
    parser: &mut Parser,
    enum_name: String,
) -> Result<EnumDecl> {
    parser.expect(&Token::Enum)?;
    parse_enum(parser, enum_name)
}

/// Parse enum body ({ Variant1, Variant2(T), ... })
fn parse_enum(parser: &mut Parser, enum_name: String) -> Result<EnumDecl> {
    let location = parser.current_location();

    // Parse generic type parameters
    let mut generics = if parser.consume(&Token::Less)? {
        let mut generics = Vec::new();

        if !parser.check(&Token::Greater) {
            loop {
                if let Some(Token::Identifier(generic_name)) = parser.peek().map(|t| &t.token) {
                    let name = generic_name.clone();
                    parser.advance()?;

                    // Parse constraints
                    let constraints = if parser.consume(&Token::Colon)? {
                        let mut constraints = Vec::new();

                        loop {
                            if let Some(constraint) = parse_type(parser)? {
                                constraints.push(constraint);
                            }

                            if !parser.consume(&Token::Plus)? {
                                break;
                            }
                        }

                        constraints
                    } else {
                        vec![]
                    };

                    generics.push(GenericParam {
                        name,
                        constraints,
                        location: parser.current_location(),
                    });
                }

                if !parser.consume(&Token::Comma)? {
                    break;
                }
            }
        }

        parser.expect(&Token::Greater)?;
        generics
    } else {
        vec![]
    };

    // Optional where-clause before '{'
    parse_where_clause_for_generics(parser, &mut generics)?;

    parser.expect(&Token::LeftBrace)?;

    // Parse variants
    let mut variants = Vec::new();

    while !parser.check(&Token::RightBrace) {
        if let Some(Token::Identifier(variant_name)) = parser.peek().map(|t| &t.token) {
            let name = variant_name.clone();
            let variant_location = parser.current_location();
            parser.advance()?;

            // Check if this variant has data
            let data = if parser.consume(&Token::LeftParen)? {
                // Tuple-like variant: Variant(T1, T2, ...)
                let mut types = Vec::new();

                if !parser.check(&Token::RightParen) {
                    loop {
                        if let Some(field_type) = parse_type(parser)? {
                            types.push(field_type);
                        } else {
                            return Err(CompilerError::syntax(
                                parser.current_location().line,
                                parser.current_location().column,
                                "Expected type in variant tuple",
                            ));
                        }

                        if !parser.consume(&Token::Comma)? {
                            break;
                        }
                    }
                }

                parser.expect(&Token::RightParen)?;
                Some(EnumVariantData::Tuple(types))
            } else if parser.consume(&Token::LeftBrace)? {
                // Struct-like variant: Variant { field1: T1, field2: T2 }
                let mut fields = Vec::new();

                while !parser.check(&Token::RightBrace) {
                    if let Some(Token::Identifier(field_name)) = parser.peek().map(|t| &t.token) {
                        let field_name_str = field_name.clone();
                        parser.advance()?;

                        // Expect colon for struct-style fields
                        parser.expect(&Token::Colon)?;

                        // Parse field type
                        let field_type = parse_type(parser)?;
                        if field_type.is_none() {
                            return Err(CompilerError::syntax(
                                parser.current_location().line,
                                parser.current_location().column,
                                "Expected field type",
                            ));
                        }

                        fields.push(StructField {
                            name: field_name_str,
                            field_type: field_type.unwrap(),
                            default_value: None,
                            field_tag: None,
                            location: parser.current_location(),
                        });

                        // Optional comma
                        parser.consume(&Token::Comma)?;
                    } else {
                        break;
                    }
                }

                parser.expect(&Token::RightBrace)?;
                Some(EnumVariantData::Struct(fields))
            } else {
                // Unit variant: Variant
                None
            };

            variants.push(EnumVariant {
                name,
                data,
                location: variant_location,
            });

            // Optional comma between variants
            parser.consume(&Token::Comma)?;
        } else {
            // Skip unknown tokens
            parser.advance()?;
        }
    }

    parser.expect(&Token::RightBrace)?;

    Ok(EnumDecl {
        name: enum_name,
        generics,
        variants,
        location,
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::parser::Parser;

    fn parse_enum_decl(input: &str) -> Result<EnumDecl> {
        let mut parser = Parser::new(input.to_string(), None);
        parser.advance()?;
        parse_enum_declaration(&mut parser)
    }

    #[test]
    fn test_parse_simple_enum() {
        let result = parse_enum_decl("type Status enum { Pending, Completed }");
        assert!(result.is_ok());
        let decl = result.unwrap();
        assert_eq!(decl.name, "Status");
        assert_eq!(decl.variants.len(), 2);
        assert_eq!(decl.variants[0].name, "Pending");
        assert_eq!(decl.variants[1].name, "Completed");
        assert!(decl.variants[0].data.is_none()); // Unit variant
        assert!(decl.variants[1].data.is_none());
    }

    #[test]
    fn test_parse_enum_with_tuple_variant() {
        let result = parse_enum_decl("type Result enum { Ok(int), Err(string) }");
        assert!(result.is_ok());
        let decl = result.unwrap();
        assert_eq!(decl.variants.len(), 2);

        // Check first variant has tuple data
        if let Some(crate::ast::decl::EnumVariantData::Tuple(types)) = &decl.variants[0].data {
            assert_eq!(types.len(), 1);
        } else {
            panic!("Expected tuple variant");
        }
    }

    #[test]
    fn test_parse_enum_with_struct_variant() {
        let result =
            parse_enum_decl("type Result enum { Ok { value: int }, Err { message: string } }");
        assert!(result.is_ok());
        let decl = result.unwrap();
        assert_eq!(decl.variants.len(), 2);

        // Check first variant has struct data
        if let Some(crate::ast::decl::EnumVariantData::Struct(fields)) = &decl.variants[0].data {
            assert_eq!(fields.len(), 1);
            assert_eq!(fields[0].name, "value");
        } else {
            panic!("Expected struct variant");
        }
    }

    #[test]
    fn test_parse_enum_with_generics() {
        let result = parse_enum_decl("type Option enum<T> { None, Some(T) }");
        assert!(result.is_ok());
        let decl = result.unwrap();
        assert_eq!(decl.generics.len(), 1);
        assert_eq!(decl.generics[0].name, "T");
    }

    #[test]
    fn test_parse_enum_no_name_error() {
        let result = parse_enum_decl("type enum { A, B }");
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_enum_mixed_variants() {
        let result = parse_enum_decl(
            "type Event enum { Click(int, int), KeyPress { key: string }, MouseMove }",
        );
        assert!(result.is_ok());
        let decl = result.unwrap();
        assert_eq!(decl.variants.len(), 3);

        // First is tuple variant
        assert!(matches!(
            decl.variants[0].data,
            Some(crate::ast::decl::EnumVariantData::Tuple(_))
        ));
        // Second is struct variant
        assert!(matches!(
            decl.variants[1].data,
            Some(crate::ast::decl::EnumVariantData::Struct(_))
        ));
        // Third is unit variant
        assert!(decl.variants[2].data.is_none());
    }
}
